﻿using Devil;
using System.Text.RegularExpressions;
using UnityEngine;

namespace GameToolkit
{
    public enum EAssetQoS
    {
        Relax,
        Busy = 100,
        Emergency = 1000,
    }

    public delegate void AssetHandler<in T>(IAssetPtr id, T asset) where T : Object;
    public delegate void ErrorHandler(IAssetPtr id, string error);

    /// <summary>
    /// 资源引用
    /// </summary>
    public interface IAssetPtr
    {
        int Id { get; }
        string Name { get; }
        Object Asset { get; }
        ELoadResult LoadResult { get; }
        bool IsReferenced { get; }
        void Retain();
        void Release();
    }

    public interface IAssetQoS
    {
        EAssetQoS QoS { get; set; }
    }

    public interface IAssetCache<T> : IAssetPtr, IAssetQoS where T : Object
    {
        string text { get; }
        byte[] bytes { get; }
        //T AssetInstance { get; }
        void Regist(AssetHandler<T> handler, ErrorHandler errHandler);
        void Unregist(AssetHandler<T> handler, ErrorHandler errHandler);
    }

    public class AssetRef<T> : IAssetPtr, System.IEquatable<AssetRef<T>> where T : Object
    {
        private uint refCount;
        public readonly T asset;

        public string Name { get { return asset == null ? "Missing" : asset.name; } }
        public Object Asset { get { return asset; } }
        public uint ReferenceCount { get { return refCount; } }
        public bool IsReferenced { get { return refCount > 0; } }
        public ELoadResult LoadResult { get; private set; }
        public int Id { get; private set; }

        public AssetRef(T asset)
        {
            this.asset = asset;
            Id = asset.GetInstanceID();
            refCount = 0;
            LoadResult = asset == null ? ELoadResult.Faild : ELoadResult.Success;
        }

        public bool Equals(AssetRef<T> other)
        {
            return other != null && asset == other.asset;
        }

        public override bool Equals(object obj)
        {
            if (asset == obj)
                return true;
            else if (obj is AssetRef<T>)
                return asset == ((AssetRef<T>)obj).asset;
            else
                return false;
        }

        public override int GetHashCode()
        {
            return asset.GetHashCode();
        }

        public virtual void Retain()
        {
            refCount++;
        }

        public virtual void Release()
        {
            refCount--;
        }

    }


    public struct AssetID : System.IEquatable<AssetID>
    {
        public int id;
        public bool useAb;
        public string path;
        static readonly Regex RES_REGEX = new Regex("(/|^)resources(/|$)", RegexOptions.IgnoreCase);

        public bool IsVaild { get { return id != 0; } }

        public static int HashID(string path)
        {
            return ParallelUtils.IgnoreCaseToHash(path);
        }

        public static AssetID Auto(string path)
        {
            var match = RES_REGEX.Match(path);// Regex.Match(path, "(/|^)resources(/|$)", RegexOptions.IgnoreCase);
            var i = match.Length > 0 ? (match.Length + match.Index) : -1;
            AssetID aid;
            var res = i > 0 || !(path.StartsWithIgnoreCase("assets/") || path.StartsWithIgnoreCase("packages/"));
            aid.useAb = !res;
            if (res)
            {
                var e = path.LastIndexOf('.');
                if (i >= 0 && e > i)
                    aid.path = path.Substring(i, e - i);
                else if (i >= 0 && e <= i)
                    aid.path = path.Substring(i);
                else
                    aid.path = path;
            }
            else
            {
                aid.path = path;
            }
            aid.id = ParallelUtils.IgnoreCaseToHash(aid.path);
            return aid;
        }

        public static AssetID AbAsset(string path)
        {
            AssetID aid;
            aid.useAb = true;
            aid.path = path;
            aid.id = ParallelUtils.IgnoreCaseToHash(path);
            return aid;
        }

        public static AssetID ResAsset(string path)
        {
            AssetID aid;
            aid.useAb = false;
            aid.path = path;
            aid.id = ParallelUtils.IgnoreCaseToHash(path);
            return aid;
        }

        public override string ToString()
        {
            return string.Format("{0} @\"{1}\"", useAb ? "AbAsset" : "Resource", path);
        }

        public override bool Equals(object obj)
        {
            if (obj is AssetID)
                return id == ((AssetID)obj).id;
            else
                return false;
        }

        public override int GetHashCode()
        {
            return id;
        }

        public bool Equals(AssetID other)
        {
            return this.id == other.id && this.useAb == other.useAb;
        }

        public static bool operator ==(AssetID a, AssetID b)
        {
            return a.id == b.id && a.useAb == b.useAb;
        }

        public static bool operator !=(AssetID a, AssetID b)
        {
            return a.id != b.id || a.useAb != b.useAb;
        }

        public static explicit operator int(AssetID id)
        {
            return id.id;
        }
    }

}